DAs in C
Volume Number: 1
Issue Number: 11
Column Tag: Programmer's Forum
Desk Accessories in C 
By Wesley Swannack, Computer Engineer, Richland, WA.
Creating a tools and mini- applications
A feature of the Macintosh that sets it apart from other computers is it's ability
to have Desk Accessories. Desk Accessories can be considered a 'mini- application' that
can be executed while using a program such as MacDraw or writing a Macintosh
application program. Desk accessories are small programs that help you achieve a
main goal, and is a part of the Desktop Concept that Apple has envisioned. In short,
while creating programs or documents there are tools that we use intermittently, such
as the Scrapbook, or a Hex Calculator. This is analogous to activities before the advent
of personnel computers, tools physically sat on the desk, now the tools are more
powerful electronic versions.
Can useful Desk Accessories be Written?
Desk Accessories can be very powerful; enhancing the functionality of the
Macintosh. My favorite example, is Click-On Worksheet from T/Maker Graphics.
Click-On Worksheet is a very powerful spreadsheet and chart program, that allows
the creation of a small spreadsheet or chart while using a word processing or database
program. Another example is the dCad Calculator from Desktop, a scientific and
programing calculator.
Some Background Information
When writing that first desk accessory, there are two very important chapters
in Inside Macintosh, to familiarize yourself with. The Chapters are the Desk Manager
and the Device Manager. Briefly, the sole purpose of the Desk Manager is to coordinate
activities between the executing desk accessory and the application program. The
Device Manager handles the I/O of information to and from the desk accessory.
When writing a desk accessory, the program content is considered a 'DRVR'
(driver) resource type, from a Macintosh viewpoint. 'DRVR' resources are kept in the
System file and are read into memory by the Device Manager as needed. At this time
the resource ID's of the DRVR's can range from 0 to 31, however remember that the
first 11 are reserved by Apple. These reserved ID's are for things like the Disk,
Sound, AppleTalk, and Printer drivers.
Open, Close, Control, Prime, and Status
A 'DRVR' routine is made up of 5 routines that dictate actions. They are Open,
Close, Control, Prime, and Status. The Open routine reads all the 'DRVR' code and
resources into memory, creates data structures and variables when the desk accessory
is executed for the first time. The Close routine deletes and removes from memory,
structures and resources that the desk accessory used. The Control routine is the most
important of all five routines. This routine is executed everytime the procedure
SystemTask() is called within the event loop of the application program currently
running. Think of the procedure as giving control of the Mac over to the desk
accessory; then, when the desk accessory is closed, control is handed back to the main
application program. The Prime and Status routines deal with the reading and writing
of information to the 'DRVR' and at this time are not important to us. One neat feature
of Desk Accessories is the ability to attach and use resources such as WIND's and
MENU's.
Getting Started on the Desk Accessory
Ok, Let us get started on the Desk Accessory. This desk accessory has been
written using 'C' as the language and most C compilers support the creating of desk
accessories. Even if you don't program in C, most programs are easy to follow because
of all the toolbox calls that programs use. The type of desk accessory that we will be
writing is called a Shell. Shells allow a person to start from a point that works. Then
in a modular fashion new routines and functions, can be added. This allows you to track
down errors and debug the program much easier. So once you have finished with this
shell program, you will have a base to work from as you try out your own desk
accessories.
Looking at the beginning of the program listing, the necessary header files have
been included, that represent predefined constants and variable structures. Next is a
procedure called ACC(). This is a MegaMax dependent item since the Linker supports
the creation of desk accessories. What is important is the arguments of the ACC
procedure.
The Control routine can respond to certain device routines, which are determined
by the argument drvrFlags. Explanation of the flags can be found on pages 19 and 20 of
the Device Manager Guide. How one determines the drvrFlags is tersely explained in
Inside Macintosh, so after much thinking the light came on!! At the top of page 20 is a
set of predefined constants such as dNeedTime. Looking at the equates one sees that the
constants range from 0 to 6. These numbers represent which bit in the high word byte
of the drvrFlags to set to a one. So the program example $2400 in binary form
represents a 00100100 00000000. This binary number, (using page 20 as a
guide), shows that the driver can respond to control events and that the driver needs
time to perform periodic action. [Hence bit settings in the drvrFlags argument
determine characteristics of the driver. --Ed.] The next argument drvrDelay contains
a tick count indicating how often the control routine should be called. A tick count of 0
means that the control routine will be called as often as possible. A 1 means that the
action should be called every 60th of a second, 2 means every 30th of a second. Using
a value of 60 in the program, our control routine will be called every full second.
Remember whether this action occurs, is dependent on how long it takes the application
program to call SystemTasks(). DrvrEvmask is a mask that filters what type of
events the desk accessory will responded to; these are explained on page 16 of the
Event Manager. Basically, just add up all the numbers that represent those events you
want to respond to. The argument drvrMenu represents the resource ID number of a
menu that your desk accessory uses. You have seen many desk accessories that have a
menu appear on the menubar such as Extras. To attach resources such as menus and
windows, a special formula is used in determining the resource ID number. This
formula is discussed on page 10 of the Resource Manger. In our example program, a
resource number of -15424 is used. Looking at the chart on page 10 we know that
bits 15 and 14 are set to a 1. This means that all resources of desk accessories will be
negative value, such as the above example. Next bits 13, 12, and 11 are set to 0. This
specifies that a DRVR resource own's the attached resources. Bits 5 thru 10 contain
the resource id number of the DRVR. As mention in the very beginning of the program
listing, we are using a resource id = 30. Bits 4 thru 0 are variable and allow us to
have several menus attached to the same desk accessory. So the value -15424 =
11000011 11000000 in binary form. We used a variable of 0 in this MENU
resource however if you want to add another menu you can use a 1, then 2, and so on.
Length and Window title are arguments that are used for the title of the window and the
length of the window title.
Format of the Desk Accessory File
The internal format of the desk accessory file can be found on page 12 of the Desk
Manager. This is the final form that all desk accessories look like when finally
compiled into machine code. If you have a compiler that creates Desk Accessories then
this information is just for interest only.
Opening the DRVR routine
When a desk accessory is used for the first time, information about the desk
accessory is read into a structure in memory called a Device Control Entry block. An
illustrated version of this can be found on page 21 of the Device Manager. Looking at
it, we notice that it has information about the window, menu, number of ticks, etc.
This is very important, for this is how data can be accessed from one routine to the
other. The first thing that is declared is a *dctl and a *pb pointers after the
accopen(dctl,pb) function. The pointers allows access to the Device Control Block, and
since this is a RAM driver (i.e. loaded from disk into memory), a paramunion block
must be used to gain access to events and other information. Next a menu structure and
a window pointer is created since this desk accessory will have a window and a menu
associated with it. Then we check to make sure that that this desk accessory has not
been already executed by checking for the presence of the window. Once the IF
statement is true, use the function getmenu to get the menu resource that is already in
the system file. Since the device control entry block already contains the menu
resource id, just pass the value to the function getmenu. Next create the window, set
the port, and assign the text mode. Now we get the device reference number
(dctlrefnum) and insert into the windowkind field of my window structure. This
reference number is what the Device Manager uses internally, instead of using the
name of the Desk Accessory. Now assign the dcltwindow field in the device entry
block, a pointer that points to my window. At this point we have created all the menus
and windows and the Open routine is finished.
Controlling the DRVR routine
The Control routine represents the heart of the desk accessory. This routine is
executed everytime a SystemTask is called from the application program. This desk
accessory creates a window, menu, and prints the time of day in the window.
Following the program, create *dctl and *pb pointers to the various information
structures of the driver (i.e. desk accessory.) Then we create a new menu structure
and window pointer. Using the dclt (device control block) we can extract the
menuhandle and the pointer to my desk accessory's window, that are somewhere,
floating out in memory. Next is the event structure that determines what the desk
accessory will do every time the procedure SystemTask is called from the application
program.
The action taken by the Control routine is determined by a message that is stored
in .cscode field of the paramunion.cntrlparam structure. This field is accessed by the
pb pointer that was created when the Control routine was just executing. The messages
that we can expect to receive can be found on page 14 of the Desk Manager. These
messages simply tell the Control routine what kind of action to take. In our example
we used the accrun, acc event, and the accmenu messages. As outlined in the page 14,
accrun handles periodic actions such as getting the time for a clock. Handling events
such as keyboard and mouse events the accevent message is used. When using menus